package be.telio.freeagent.api.impl;

import be.betv.common.util.FormatUtils;
import be.telio.freeagent.api.FreeAgentService;
import be.telio.freeagent.model.*;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import java.io.ByteArrayOutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Date;

public class FreeAgentServiceImpl implements FreeAgentService
{
    private String apiHost;
    private String apiUsername;
    private String apiPassword;
    private Client client;

    public void initialize()
    {
        HTTPBasicAuthFilter authFilter = new HTTPBasicAuthFilter(apiUsername, apiPassword);
        client = Client.create();
        client.addFilter(authFilter);
    }

    public Client getClient()
    {
        return client;
    }

    public FAInvoices getInvoices()
    {
        WebResource webResource = client.resource(getUrl("/invoices"));
        return webResource.accept("application/xml").get(FAInvoices.class);
    }

    public FAInvoice getInvoice(int id)
    {
        WebResource webResource = client.resource(getUrl("/invoices/" + id));
        return webResource.accept("application/xml").get(FAInvoice.class);
    }

    public FAAttachments getAttachments()
    {
        WebResource webResource = client.resource(getUrl("/attachments"));
        return webResource.accept("application/xml").get(FAAttachments.class);
    }

    public FABills getBills(Date from, Date to)
    {
        WebResource webResource = client.resource(getUrl("/bills?view=" + FormatUtils.FORMAT_YMD_DASH.format(from) + "_" + FormatUtils.FORMAT_YMD_DASH.format(to)));
        return webResource.accept("application/xml").get(FABills.class);
    }

    public FABankAccounts getBankAccounts()
    {
        WebResource webResource = client.resource(getUrl("/bank_accounts"));
        return webResource.accept("application/xml").get(FABankAccounts.class);
    }

    public FATasks getTasks()
    {
        WebResource webResource = client.resource(getUrl("/tasks"));
        return webResource.accept("application/xml").get(FATasks.class);
    }

    public FAProjects getProjects()
    {
        WebResource webResource = client.resource(getUrl("/projects"));
        return webResource.accept("application/xml").get(FAProjects.class);

    }

    public FAContacts getContacts()
    {
        WebResource webResource = client.resource(getUrl("/contacts"));
        return webResource.accept("application/xml").get(FAContacts.class);
    }

    public <T> T getEntityList(Class<T> clazz)
    {
        try
        {
            FAEntityList e = (FAEntityList) clazz.newInstance();
            WebResource webResource = client.resource(getUrl("/" + e.getEntityListName()));
            return webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).get(clazz);
        }
        catch (Exception e1)
        {
            if (!e1.getMessage().contains("nil-classes"))
            {
                e1.printStackTrace();
            }
        }

        return null;
    }

    public <T> T getEntity(Class<T> clazz, Integer id)
    {
        try
        {
            FAEntity e = (FAEntity) clazz.newInstance();
            WebResource webResource = client.resource(getUrl("/" + e.getEntityListName() + "/" + id));
            return webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).get(clazz);
        }
        catch (Exception e1)
        {
            return null;
        }
    }

    public <T> boolean deleteEntity(T entity)
    {
        FAEntity e = (FAEntity) entity;
        WebResource webResource = client.resource(getUrl("/" + e.getEntityListName() + "/" + e.getId()));
        ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).delete(ClientResponse.class, entity);
        return response.getStatus() == 200;
    }
    public <T> T addEntity(T entity)
    {
        FAEntity e = (FAEntity) entity;
        WebResource webResource = client.resource(getUrl("/" + e.getEntityListName()));

        ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(ClientResponse.class, entity);

        return (T) getEntity(response.getLocation(), entity.getClass());
    }

    public <T> T updateEntity(T entity)
    {
        FAEntity e = (FAEntity) entity;
        WebResource webResource = client.resource(getUrl("/" + e.getEntityListName() + "/" + e.getId()));
        ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(ClientResponse.class, entity);
        return (T) getEntity(response.getLocation(), entity.getClass());
    }

    protected <T> T getEntity(URI uri, Class<T> clazz)
    {
        if (uri != null)
        {
            String url = uri.toString();
            if (!uri.isAbsolute())
            {
                WebResource webResource = client.resource(getUrl(url));
                return webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).get(clazz);
            }
            else
            {
                WebResource webResource = client.resource(uri);
                return webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).get(clazz);
            }
        }
        else
        {
            return null;
        }
    }

    public FAContact addContact(FAContact contact)
    {
        WebResource webResource = client.resource(getUrl("/contacts"));
        ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(ClientResponse.class, contact);
        return getEntity(response.getLocation(), FAContact.class);
    }

    public FAContact updateContact(FAContact contact)
    {
        WebResource webResource = client.resource(getUrl("/contacts/" + contact.id));
        ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(ClientResponse.class, contact);
        return getEntity(response.getLocation(), FAContact.class);
    }

    public void addInvoice(FAInvoice invoice)
    {
        WebResource webResource = client.resource(getUrl("/invoices"));
        String string = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(String.class, invoice);
        System.out.println(string);
    }

    private String getUrl(String resource)
    {
        return "https://" + apiHost + resource;
    }

    public void setApiHost(String apiHost)
    {
        this.apiHost = apiHost;
    }

    public void setApiUsername(String apiUsername)
    {
        this.apiUsername = apiUsername;
    }

    public void setApiPassword(String apiPassword)
    {
        this.apiPassword = apiPassword;
    }

    private void debug(Object o)
    {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try
        {
            JAXBContext jc = JAXBContext.newInstance(o.getClass());
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            m.marshal(o, out);
        }
        catch (Exception e)
        {
            throw new Error("oops ...", e);
        }

        System.out.println(out.toString());

    }

}
